﻿using System;
using System.Linq;
using System.Collections.Generic;
using TinyEngine.Core.Components;
using TinyEngine.Core.Entities;
using TinyEngine.Core.Camera;
using Microsoft.Xna.Framework;

namespace TinyEngine.Core.Scene
{
    public class TinyScene
    {
        public string AssetsPath { get; set; }

        private TinySceneGraph graph;

        public TinySceneGraph Graph
        {
            get { return graph; }
        }

        public TinyScene()
        {
            this.graph = new TinySceneGraph(this);
            this.Templates = new List<IEntity>();
            this.Components = new List<ITinySceneComponent>();
        }
        
        public void AddEntity(IEntity entity)
        {
            
            IterateForCameras(entity);

            itemsToAdd.Add(entity);
        }

        private void IterateForCameras(IEntity entity)
        {
            if (entity is ICamera)
                Cameras.Add(entity as ICamera);

            foreach (var item in entity.Children)
            {
                IterateForCameras(item);
            }
        }

        public void RegisterNewItems()
        {
            foreach (var item in this.itemsToAdd)
            {
                item.Register(this);
                this.graph.Entities.Add(item);

                RegisterChildren(item);
                foreach (var comp in item.Components)
                {
                    comp.Register();
                }
            }
            this.itemsToAdd.Clear();
        }

        private void RegisterChildren(IEntity item)
        {
            foreach (var child in item.Children)
            {
                child.Register(this);
                RegisterChildren(child);
                foreach (var comp in child.Components)
                {
                    comp.Register();
                }
            }
        }
        
        internal List<IEntity> itemsToAdd = new List<IEntity>();

        public List<IEntity> Templates { get; set; }

        public IEntity CreateTemplateClone(string templateName)
        {
            foreach (var item in this.Templates)
            {
                if (item.Name == templateName)
                {
                    ICopyable clonable = item as ICopyable;
                    var ent = item.GetType().Assembly.CreateInstance(item.GetType().FullName) as IEntity;
                    clonable.CopyTo(ent);

                    foreach (var comp in item.Components)
                    {
                        ICopyable compCopy = comp as ICopyable;
                        if (compCopy != null)
                        {
                            var c = Activator.CreateInstance(comp.GetType(), ent) as ITinyComponent;
                            compCopy.CopyTo(c);

                            ent.Components.Add(c);
                        }
                    }
                    return ent;
                }
            }
            return null;
        }

        private List<ICamera> cameras = new List<ICamera>();
        
        public List<ICamera> Cameras
        {
            get { return cameras; }
        }

        public ICamera FirstCamera
        {
            get
            {
                return cameras.FirstOrDefault();
            }
        }


        public T GetComponent<T>() where T : class
        {
            if (Components == null)
                return null;

            foreach (var item in Components)
            {
                if (item.GetType() == typeof(T))
                {
                    return item as T;
                }
            }
            return null;
        }
        public List<ITinySceneComponent> Components { get; set; }

        internal void DeleteItems()
        {
            foreach (var item in markedForDelete)
            {
                if (item.Parent != null)
                    item.Parent.Children.Remove(item);
                else
                    this.graph.Entities.Remove(item);
            }

            markedForDelete.Clear();
        }

        List<IEntity> markedForDelete = new List<IEntity>();
        internal void MarkEntityForDelete(IEntity item)
        {
            markedForDelete.Add(item);
        }
    }

    public class EntityList : List<IEntity>
    {
        public TinyScene SceneOwner;
        public new void Add(IEntity entity)
        {
            
            SetSceneOwnerAndChildren(entity);
            base.Add(entity);
        }

        private void SetSceneOwnerAndChildren(IEntity entity)
        {
            entity.SetScene(SceneOwner);
            foreach (var item in entity.Children)
            {
                SetSceneOwnerAndChildren(item);
            }
        }
    }

    public class TinySceneGraph
    {
        private EntityList entities = new EntityList();
        public EntityList Entities
        {
            get { return entities; }
        }
        public TinyScene SceneOwner;
        public TinySceneGraph(TinyScene owner)
        {
            this.SceneOwner = owner;
            entities.SceneOwner = this.SceneOwner;
        }



        public T1 FindEntity<T1>(string name) where T1 : class
        {
            foreach (var item in this.entities)
            {
                if (item.Name == name)
                    return item as T1;

                var itm = FindInChildren<T1>(item, name);
                if (itm != null)
                    return itm as T1;
            }
            return null;
        }

        private T FindInChildren<T>(IEntity item, string name) where T : class
        {
            foreach (var child in item.Children)
            {
                if (child.Name == name)
                    return child as T;

                var found = FindInChildren<T>(child, name) as T;
                if (found != null)
                    return found;
            }
            return null;
        }

        public IEnumerable<IEntity> FindEntitiesAtPosition(Microsoft.Xna.Framework.Vector2 pos)
        {
            List<IEntity> items = new List<IEntity>();

            foreach (var item in this.Entities)
            {
                RecursiveGetEntitiesAtPosition(pos.ToPoint(), item, items);
                
            }
            return items;
        }

        private void RecursiveGetEntitiesAtPosition(Point pos, IEntity item, List<IEntity> items)
        {
            if (item.BoundingRectangle.Contains(pos))
            {
                items.Add(item);
            }

            if (item.Children.Count > 0)
            {
                foreach (var child in item.Children)
                {
                    RecursiveGetEntitiesAtPosition(pos, child, items);
                }
            }
        }

        public IEnumerable<IEntity> EveryItem()
        {
            var list = new List<IEntity>();
            list.AddRange(IterateItems(entities));
            return list;
        }

        private IEnumerable<IEntity> IterateItems(List<IEntity> entities)
        {
            var list = new List<IEntity>();
            foreach (var item in entities)
            {
                list.Add(item);
                list.AddRange(IterateItems(item.Children));
            }
            return list;
        }

        public void Remove(IEntity selectedEntity)
        {
            if (selectedEntity.Parent == null)
                return;

            selectedEntity.Parent.Children.Remove(selectedEntity);
        }
    }
}
